package scfg.utils.training_set_filter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import desposito6.utils.CommandLine;

import scfg.AlignedSequence;
import scfg.output.Display;

public class IdenityFilter {
	
	public static boolean IDENTITY_ON_ALIGNMENT = false;
	public static boolean PENALIZE_FOR_SPACE = false;

	public static double percentIdentity(String a, String b) {
		return 1 - ((double) editDistance(a, b) / (double)Math.min(a.length(), b.length())); 
	}

	public static double percentIdentity2(String a, String b) {
		int gapCount = 0;
		StringBuilder sbA = new StringBuilder();
		StringBuilder sbB = new StringBuilder();
		for (int i=0;i<a.length() && i<b.length();i++){
			if (a.charAt(i) == b.charAt(i) && a.charAt(i) == '-') 
				gapCount++;
			else {
				sbA.append(a.charAt(i));
				sbB.append(b.charAt(i));
			}
		}
		int editDist = editDistance2(sbA.toString(), sbB.toString());
		double val = (double) editDist / (double)(sbA.length());
		return 1 - (val); 
	}
	
	public static double percentIdentity3(String a, String b) {
		StringBuilder sbA = new StringBuilder();
		StringBuilder sbB = new StringBuilder();
		for (int i=0;i<a.length() && i<b.length();i++){
			if (!(a.charAt(i) == b.charAt(i) && a.charAt(i) == '-')) {
				sbA.append(a.charAt(i));
				sbB.append(b.charAt(i));
			}
		}
		return 1 - ((double) hammingDistance(sbA.toString(), sbB.toString()) / (double)Math.min(sbA.length(), sbB.length())); 
	}
	
	public static int editDistance(String a, String b) {
		int[][] arr = new int[a.length()+1][b.length()+1];
		for(int i=0;i<=a.length();i++)
			arr[i][0] = i;
		for(int i=0;i<=b.length();i++)
			arr[0][i] = i;
		for(int i=1;i<=a.length(); i++) {
			for (int j=1;j<=b.length(); j ++) {
				if (a.charAt(i-1) == b.charAt(j-1) || (!PENALIZE_FOR_SPACE &&  ( a.charAt(i-1) == '-' || b.charAt(j-1) == '-')))
					arr[i][j] = arr[i-1][j-1];
				else
					arr[i][j] = Math.min(arr[i-1][j], arr[i][j-1]) + 1;
			}
		}
		return arr[a.length()][b.length()];
	}
	

	
	public static int editDistance2(String a, String b) {
		int[][] arr = new int[a.length()+1][b.length()+1];
		for(int i=0;i<=a.length();i++)
			arr[i][0] = i;
		for(int i=0;i<=b.length();i++)
			arr[0][i] = i;
		for(int i=1;i<=a.length(); i++) {
			for (int j=1;j<=b.length(); j ++) {
				if (a.charAt(i-1) == b.charAt(j-1))
					arr[i][j] = arr[i-1][j-1];
				else {
					arr[i][j] = Math.min(Math.min(arr[i-1][j], arr[i][j-1]) + 1, arr[i-1][j-1] + 1);
//					arr[i][j] = Math.min(arr[i-1][j], arr[i][j-1]) + 1;
				}
			}
		}
		return arr[a.length()][b.length()];
	}
	
	public static int hammingDistance(String a, String b) {
		int cnt = 0;
		for(int i=0;i<a.length() && i<b.length();i++)
			if (a.charAt(i) != b.charAt(i) || a.charAt(i) == '-') 
				cnt++;
		return cnt;
	}
	
	public static List<AlignedSequence> getIdentityFilteredGroupFromSequence(List<AlignedSequence> sequences, double identityThreshold, boolean updatePercIdentity){
		List<AlignedSequence> sorted = new ArrayList<AlignedSequence>(sequences);
		Collections.sort(sorted, new AlignedSequence.ComparatorIdentity());
		List<AlignedSequence> accepted = new ArrayList<AlignedSequence>();

		System.out.println("\n\nStarted with: " + sequences.size() + "\n\n");
		for (AlignedSequence sortedSeq : sorted) {
			double max = 0;
			for (AlignedSequence acceptedSeq : accepted){
				max = Math.max(max, percentIdentity(sortedSeq.getSequence(), acceptedSeq.getSequence()));
				if (max > identityThreshold)
					break;
			}
			if (max <= identityThreshold)
				accepted.add(sortedSeq);
			else
				System.out.println(Display.buf(max+"", 10, ' ') + "\t" + sortedSeq.getName());
			if (updatePercIdentity)
				sortedSeq.setPercIden(max);
		}
		
		System.out.println("\n\nEnding with: " + accepted.size());
		return accepted;
	}
	
	public static List<AlignedSequence> getIdentityFilteredGroupFromAlignment(List<AlignedSequence> sequences, double identityThreshold, boolean updatePercIdentity, Display output){
		List<AlignedSequence> sorted = new ArrayList<AlignedSequence>(sequences);
//		Collections.sort(sorted, new AlignedSequence.ComparatorIdentity());
		Collections.sort(sorted, new AlignedSequence.ComparatorDenseComp());
		List<AlignedSequence> accepted = new ArrayList<AlignedSequence>();

		System.out.println("\n\nStarted with: " + sequences.size() + "\n\n");
//		long start = System.currentTimeMillis();
//		int count = 0;
		
		for (AlignedSequence sortedSeq : sorted) {
			double max = 0;
			String killer= null;
//			CommandLine.DisplayBar(sorted.size(), count++, (System.currentTimeMillis() - start ) / 1000 );
			for (AlignedSequence acceptedSeq : accepted){
//				max = Math.max(max, percentIdentity2(sortedSeq.getAlignment(), acceptedSeq.getAlignment()));
				max = Math.max(max, percentIdentity3(sortedSeq.getAlignment(), acceptedSeq.getAlignment()));
				if (max > identityThreshold) {
					killer = acceptedSeq.getName();
					break;
				}
			}
			if (max <= identityThreshold)
				accepted.add(sortedSeq);
			else
				output.out(Display.buf(max+"", 10, ' ') + Display.buf(sortedSeq.getName(), 40, ' ') + (killer != null ? Display.buf(killer, 40, ' ') : ""));
			if (updatePercIdentity)
				sortedSeq.setPercIden(max);
			
		}
		output.finalizeDisplay();
//		CommandLine.DisplayBarFinish();
		
		System.out.println("\n\nEnding with: " + accepted.size());
		return accepted;
	}
}
